import { Injectable, OnModuleInit } from '@nestjs/common'
import { VersionBranchRepo, VersionFlowRepo, VersionRepo, VersionRepoRepo } from '@/entities'
import { CoreException, OaExceptionType } from '@/modules'
import { GlobalConfigService } from '@/service'
import { snowflake, timestampFormat, uuid } from '@/util'
import { IBodyUser, IBranch, IFlow, IPagesOption, IRepo, IVersion } from '@/interface'
import moment from 'moment'
/**
 * @Description: 版本管理
 * @author Oak Chen
 * @date 2021/2/5
 */
interface Notify {
    br: string
    bn: string
    pp: string
    rp: string
    pj: string
}

const defaultBranch = [
    { branch: 'develop', companyid: '0000', branchCur: '开发环境' },
    { branch: 'uat/bty', companyid: '0000', branchCur: '预发环境' },
    { branch: 'prod/bty', companyid: '0000', branchCur: '生产环境' },
]

@Injectable()
export class VersionService implements OnModuleInit {
    constructor(
        private readonly versionRepo: VersionRepo,
        private readonly versionRepoRepo: VersionRepoRepo,
        private readonly versionBranchRepo: VersionBranchRepo,
        private readonly versionFlowRepo: VersionFlowRepo,
        private readonly globalConfigService: GlobalConfigService,
    ) {}
    public async onModuleInit() {
        const count = await this.versionBranchRepo.count()
        if (count === 0) {
            for await (const b of defaultBranch) {
                await this.versionBranchRepo.save(
                    this.versionBranchRepo.create({
                        ...b,
                        branchid: snowflake(),
                        au: 'system',
                        ct: timestampFormat(),
                    }),
                )
            }
        }
    }

    public async notify(option: Notify) {
        const build = decodeURI(option.bn)
        const pipeline = decodeURI(option.pp)
        const repo = decodeURI(option.rp)
        const branch = decodeURI(option.br)
        const projectid = decodeURI(option.pj)
        const branchInfo = await this.getOneBranch(branch)
        if (!branchInfo) {
            throw new CoreException(OaExceptionType.ERROR_OA_BRANCH_FAIL + branch)
        }
        let repoStr = ''
        if (repo && repo.includes('.git')) {
            const r = repo.split('.git')[0].split('/')
            repoStr = r[r.length - 1]
        }
        const repoInfo = await this.getOneRepo(repoStr)
        if (!repoInfo) {
            throw new CoreException(OaExceptionType.ERROR_OA_REPO_FAIL + repo)
        }
        const fix = await this.globalConfigService.getConfig('VERSION_PREFIX', projectid)
        const versionFix = `${fix}.${moment().format('MMDD')}`
        const versionName = `${versionFix}_${build}`
        const exiting = await this.versionFlowRepo.findOne({ flow: pipeline, versionName })
        if (!exiting) {
            const versionid = snowflake()
            const exitingVersion = await this.versionRepo.findOne({ version: versionFix })
            if (!exitingVersion) {
                await this.versionRepo.save(
                    this.versionRepo.create({
                        versionid,
                        version: versionFix,
                        au: 'flow',
                        ct: timestampFormat(),
                    }),
                )
            }
            await this.versionFlowRepo.save(
                this.versionFlowRepo.create({
                    flow: pipeline,
                    branchid: branchInfo.branchid,
                    repoid: repoInfo.repoid,
                    versionid: exitingVersion?.versionid || versionid,
                    projectid,
                    versionName,
                    ct: timestampFormat(),
                    au: 'flow',
                }),
            )
        }
        return {}
    }
    public async getOneBranch(branch: string) {
        return await this.versionBranchRepo.findOne({ branch })
    }
    public async getOneRepo(repoRepo: string) {
        return await this.versionRepoRepo.findOne({ repoRepo })
    }

    /**
     * 仓库管理
     * @param option
     */
    public async queryRepo(option: IRepo & IPagesOption) {
        const { current = 1, pageSize = 50, repoName, repoPath, repoRepo } = option
        const repoBuilder = this.versionRepoRepo.createQueryBuilder().where('repoid>0')
        if (repoName) repoBuilder.andWhere('repoName like :repoName', { repoName: `%${repoName}%` })
        if (repoPath) repoBuilder.andWhere('repoPath like :repoPath', { repoPath: `%${repoPath}%` })
        if (repoRepo) repoBuilder.andWhere('repoRepo like :repoRepo', { repoRepo: `%${repoRepo}%` })
        const [data, total] = await repoBuilder
            .skip((current - 1) * pageSize)
            .take(pageSize)
            .getManyAndCount()
        return {
            data,
            total,
            current,
            pageSize,
            count: data.length,
        }
    }
    public async saveRepo(option: IRepo & IBodyUser) {
        const { repoid, repoRepo, repoName, repoPath } = option
        const userName = option.__user.name
        if (option.repoPath && option.repoPath.includes('.git')) {
            const repo = option.repoPath.split('.git')[0].split('/')
            option.repoRepo = repo[repo.length - 1]
        }
        if (repoid) {
            await this.versionRepoRepo.update(
                { repoid },
                {
                    repoRepo,
                    repoName,
                    repoPath,
                    ut: timestampFormat(),
                    ed: userName,
                },
            )
        } else {
            await this.versionRepoRepo.save(
                this.versionRepoRepo.create({
                    repoName,
                    repoPath,
                    repoRepo,
                    repoid: snowflake(),
                    au: userName,
                    ct: timestampFormat(),
                }),
            )
        }
        return { message: '保存成功！' }
    }
    public async deleteRepo(option: IRepo) {
        if (option.repoid) {
            await this.versionRepoRepo.delete({ repoid: option.repoid })
        }
        return { message: '删除成功！' }
    }

    /**
     * 分支管理
     * @param option
     */
    public async queryBranch(option: IBranch & IPagesOption) {
        const { current = 1, pageSize = 50, branch, branchCur, company } = option
        const branchBuilder = this.versionBranchRepo
            .createQueryBuilder('b')
            .leftJoinAndMapOne('b.company', 'company', 'c', 'c.companyid=b.companyid')
            .where('b.branchid>0')
        if (branch) branchBuilder.andWhere('b.branch like :branch', { branch: `%${branch}%` })
        if (branchCur) branchBuilder.andWhere('b.branchCur like :branchCur', { branchCur: `%${branchCur}%` })
        if (company) branchBuilder.andWhere('c.companyName like :company', { company: `%${company}%` })
        const [data, total] = await branchBuilder
            .orderBy('b.ut', 'DESC')
            .skip((current - 1) * pageSize)
            .take(pageSize)
            .getManyAndCount()
        return {
            data,
            total,
            current,
            pageSize,
            count: data.length,
        }
    }
    public async saveBranch(option: IBranch & IBodyUser) {
        const { branch, branchCur, branchid, companyid } = option
        const userName = option.__user.name
        if (branchid) {
            await this.versionBranchRepo.update(
                { branchid },
                {
                    branch,
                    branchCur,
                    companyid,
                    ut: timestampFormat(),
                    ed: userName,
                },
            )
        } else {
            if (await this.versionBranchRepo.findOne({ branch })) {
                throw new CoreException(OaExceptionType.ERROR_OA_VERSION_BRANCH_EXITING + branch)
            }
            await this.versionBranchRepo.save(
                this.versionBranchRepo.create({
                    branchid: snowflake(),
                    branch,
                    branchCur,
                    companyid,
                    ct: timestampFormat(),
                    au: userName,
                }),
            )
        }
        return { message: '保存成功！' }
    }
    public async deleteBranch(option: IBranch) {
        const { branchid } = option
        if (branchid) {
            await this.versionBranchRepo.delete({ branchid })
        }
        return { message: '删除成功！' }
    }

    /**
     * 版本信息管理
     * @param option
     */
    public async queryVersion(option: { num: number } & IVersion & IPagesOption) {
        const { current = 1, pageSize = 50, branch, project, company, num = 5 } = option
        const versionBuilder = this.versionRepo
            .createQueryBuilder('v')
            .leftJoin('version_flow', 'f', 'f.versionid=v.versionid')
            .leftJoinAndMapOne('v.branch', 'version_branch', 'b', 'b.branchid=f.branchid')
            .leftJoinAndMapOne('v.project', 'project', 'p', 'p.projectid=f.projectid')
            .leftJoinAndMapOne('v.company', 'company', 'c', 'c.companyid=b.companyid')
            .where('v.versionid>0')
        if (branch) {
            versionBuilder.andWhere('b.branchCur like :branch', { branch: `%${branch}%` })
        }
        if (project) {
            versionBuilder.andWhere('p.projectName like :project', { project: `%${project}%` })
        }
        if (company) {
            versionBuilder.andWhere('(c.companyName like :company or c.companyUrl like :company)', {
                company: `%${company}%`,
            })
        }
        const [data, total] = await versionBuilder
            .orderBy('v.ct', 'DESC')
            .skip((current - 1) * pageSize)
            .take(pageSize)
            .getManyAndCount()
        for await (const ver of data as Record<string, any>[]) {
            ver.repo = await this.queryEnumRepo(ver.project.projectid)
            let updataCount = 0 // 更新服务计数
            for await (const rp of ver.repo) {
                rp.flow = await this.queryEnumFlow(rp.repoid, ver.company.companyid, ver.branch.branchid, num)
                if (Array.isArray(rp.flow)) {
                    let isCount = false
                    for await (const f of rp.flow) {
                        f.isUpdata = await this.isUpdata(f, ver)
                        if (!isCount && f.isUpdata) {
                            updataCount += 1
                        }
                    }
                } else {
                    rp.flow.isUpdata = await this.isUpdata(rp.flow, ver)
                    if (rp.flow.isUpdata) {
                        updataCount += 1
                    }
                }
            }
            ver.updataCount = updataCount
        }
        return {
            data,
            total,
            current,
            pageSize,
            count: data.length,
        }
    }
    private async queryEnumRepo(projectid: string) {
        return await this.versionRepoRepo
            .createQueryBuilder('r')
            .leftJoin('version_flow', 'f', 'f.repoid=r.repoid')
            .where('f.projectid=:projectid', { projectid })
            .groupBy('r.repoid')
            .getMany()
    }
    private async queryEnumFlow(repoid: string, companyid: string, branchid: string, num = 1) {
        const flowBuilder = this.versionFlowRepo
            .createQueryBuilder('f')
            .leftJoin('version_branch', 'b', 'b.branchid=f.branchid')
            .where('f.repoid=:repoid', { repoid })
            .andWhere('b.companyid=:companyid', { companyid })
            .andWhere('f.branchid=:branchid', { branchid })
            .orderBy('f.ct', 'DESC')
        if (num === 1) {
            return flowBuilder.orderBy('f.ct', 'DESC').getOne()
        } else {
            return flowBuilder.orderBy('f.ct', 'DESC').take(num).getMany()
        }
    }
    private async isUpdata(flow, ver) {
        return flow.versionName.includes(ver.version)
    }
    public async setVersionContext(option: { versionid: string; context: string } & IBodyUser) {
        const { versionid, context } = option
        const ed = option.__user.name
        await this.versionRepo.update({ versionid }, { context, ed, ut: timestampFormat() })
        return { message: '修改成功！' }
    }
}
